﻿package net.wangit.framework.dispatcher;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;

import net.wangit.Invocation;
import net.wangit.LabelUtil;
import net.wangit.context.ActionContext;
import net.wangit.context.ActionContextHelper;
import net.wangit.context.ActionContextImpl;
import net.wangit.context.ActionResult;
import net.wangit.context.LocalContext;
import net.wangit.context.LocalContextHelper;
import net.wangit.context.Message;
import net.wangit.framework.action.AbstractActionResult;
import net.wangit.framework.action.ActionMapping;
import net.wangit.framework.action.ActionMappingFactory;
import net.wangit.framework.action.DispatcherActionResult;
import net.wangit.framework.action.OutActionResult;
import net.wangit.framework.action.ProxyFactory;
import net.wangit.framework.action.RedirectActionResult;
import net.wangit.framework.config.DomainsSettings;
import net.wangit.framework.debug.Recorder;
import net.wangit.framework.session.AgileSession;
import net.wangit.framework.session.UnitDataHelper;
import net.wangit.framework.util.ResponseWriter;
import net.wangit.support.Action;
import net.wangit.support.ActionInterceptor;
import net.wangit.support.Proxy;
import net.wangit.util.Checker;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class DefaultInvocation implements Invocation {

	private static final Log log = LogFactory
			.getLog(net.wangit.framework.dispatcher.DefaultInvocation.class);
	private boolean executed;
	private ActionContext ac;
	private ActionMapping am;
	private Iterator interceptors;
	private String lastInterceptorName;

	protected DefaultInvocation(String domain, String unitId, String actionId) {
		executed = false;
		am = ActionMappingFactory.getActionMapping(domain, unitId, actionId);
		ac = LocalContext.getLocalContext().getActionContext();
		ActionContextImpl aci = (ActionContextImpl) ac;
		aci.setActionParameter(am.getParameter());
		ActionResult result = ((ActionResult) (am.isRedirect() ? ((ActionResult) (new RedirectActionResult()))
				: ((ActionResult) (new DispatcherActionResult()))));
		result.setScript(am.getScript());
		aci.setActionResult(result);
		interceptors = am.getInterceptors();
		ActionContextHelper.setMappingParameter(ac, am);
	}

	public ActionMapping getActionMapping() {
		return am;
	}

	public ActionContext getActionContext() {
		return ac;
	}

	public void invoke() throws Exception {
		if (executed)
			throw new IllegalStateException("Action has already executed");
		if (interceptors != null && interceptors.hasNext()) {
			ActionInterceptor interceptor = (ActionInterceptor) interceptors
					.next();
			if (Recorder.isEnabled()) {
				if (lastInterceptorName != null)
					Recorder.record((new StringBuilder("AI: ")).append(
							lastInterceptorName).toString());
				lastInterceptorName = interceptor.getClass().getName();
			}
			interceptor.intercept(ac, this);
		} else {
			if (Recorder.isEnabled() && lastInterceptorName != null)
				Recorder.record((new StringBuilder("AI: ")).append(
						lastInterceptorName).toString());
			doAction();
		}
		if (!executed && log.isDebugEnabled())
			log.debug((new StringBuilder("Action ")).append(am.getDomain())
					.append("/").append(am.getUnitId()).append("/").append(
							am.getId())
					.append(" was broken by an interceptor.").toString());
	}

	private void doAction() throws Exception {
		Proxy proxy = ProxyFactory.getProxy(am.getProxy());
		String actionConfig = am.getAction();
		String methodName = null;
		int index = actionConfig.indexOf("#");
		String actionName;
		if (index > 0) {
			actionName = actionConfig.substring(0, index);
			methodName = actionConfig.substring(index + 1);
		} else {
			actionName = actionConfig;
		}
		Action action = proxy.getAction(actionName);
		if (index > 0)
			ac = doMethod(action, methodName, actionConfig);
		else
			ac = action.execute(ac);
		LocalContextHelper.setActionContext(ac);
		executed = true;
	}

	private ActionContext doMethod(Action action, String methodName,
			String actionConfig) throws Exception {
		Object param[];
		Method method;
		param = (new Object[] { ac });
		Class[] classParam = { net.wangit.context.ActionContext.class };
		try {
			method = action.getClass().getMethod(methodName, classParam);
			return (ActionContext) method.invoke(action, param);
		} catch (SecurityException e) {
			log.error("类强制转换不成功，可能是将空值转换为对象", e);
			throw e;
		} catch (IllegalArgumentException e) {
			log.error((new StringBuilder("类{")).append(actionConfig).append(
					"}的方法参数不正确。").toString(), e);
			throw e;
		} catch (NoSuchMethodException e) {
			log.error((new StringBuilder("方法{")).append(actionConfig).append(
					"}调用失败。").toString(), e);
			throw e;
		} catch (IllegalAccessException e) {
			log.error((new StringBuilder("方法{")).append(actionConfig).append(
					"}不能被实例化。").toString(), e);
			throw e;
		} catch (InvocationTargetException e) {
			log.error((new StringBuilder("方法{")).append(actionConfig).append(
					"}不能抛出正确的异常。").toString(), e.getTargetException());
			throw e;
		}
	}

	protected void doResult() throws Exception {
		AbstractActionResult result = (AbstractActionResult) ac
				.getActionResult();
		if (result instanceof OutActionResult)
			return;
		setResultUrl(result);
		AgileSession session = ActionContextHelper.getSession(ac);
		saveResultMessage(session, result);
		saveActionRecord(session);
		if (!result.isSuccessful())
			UnitDataHelper.setSnapshot(ac);
		else
			session.setActionScript(result.getScript());
		if (Checker.isEmpty(result.getUrl())) {
			String successful = result.isSuccessful() ? "successfully"
					: "failed";
			ResponseWriter.writeWithHtml(LabelUtil.get(ac, "system",
					"action.can_not_forward", successful));
		} else {
			result.execute();
		}
	}

	private void saveResultMessage(AgileSession session,
			AbstractActionResult result) {
		Message message = result.getMessage();
		if (result.isSuccessful() && message.getInfos().size() == 0)
			message.addInfo(am.getSuccessfulMessage(ac));
		else if (!result.isSuccessful() && message.getErrors().size() == 0)
			message.addError(am.getFailedMessage(ac));
		session.setMessage(message);
	}

	private void saveActionRecord(AgileSession session) {
		if (Recorder.isEnabled()) {
			Recorder.end();
			session.setActionRecords(Recorder.getRecord().write("action"));
		}
	}

	private void setResultUrl(AbstractActionResult result) {
		String url = result.getUrl();
		if (Checker.isEmpty(url))
			if (result.isSuccessful())
				url = am.getSuccessfulForward();
			else
				url = am.getFailedForward();
		if (Checker.isEmpty(url))
			url = ActionContextHelper.getSession(ac).getCurrentUrl();
		if (Checker.isEmpty(url))
			url = DomainsSettings.getIndexUrl(am.getDomain());
		ActionContextHelper.getSession(ac).addAccessChain(url);
		result.setUrl(url);
	}

}
